import { ready, createModal } from '@/lib/utils';
import { initializeTable } from "../_lib/project_list";
import { get, postJson, request } from '@/lib/request';
import { FilterEditor, getValueInput, type SchemaField } from 'django-lispy-filter-ui';
import { FileHandler } from '@/lib/file';

async function popupFilterUI() {    
    const schemaResp = await get("/api/project/model_schema");
    const schema = await schemaResp.json();

    const drawerElem = document.getElementById("by6_drawer_right")! as HTMLInputElement;
    const drawerBodyElem = document.getElementById("by6_drawer_right_body")!;
    if (drawerBodyElem.childElementCount) {
        drawerElem.click();
        return;  
    }


    const containerElem = document.createElement("div");
    containerElem.classList.add("tw:w-200", "tw:h-screen", "tw:overflow-auto");
    // TODO add debug button to let user copy the `toJSON` query string.
    containerElem.innerHTML = `
<div class="tw:flex tw:gap-2">
    <button id="apply-filter-btn" class="by6-crumbs-action-btn">${gettext("应用")}</button>
    <button id="debug-filter-btn" class="by6-crumbs-action-btn">${gettext("调试")}</button>
</div>
<div></div>
`;

    drawerBodyElem.replaceChildren(containerElem);

    const editor = new FilterEditor({
        container: '#by6_drawer_right_body > div > div:nth-child(2)',
        schema: schema,
        mainModel: "Project",
        getValueInputFn: (
            lookup: string,
            field: SchemaField,
            initialValue: string | null
        ) => {
            const valueInputElem = getValueInput(lookup, field, initialValue);
            if (valueInputElem.tagName == "INPUT") {
                switch (field.class) {
                    case "YesNoField":
                        // Use type assertion to bypass readonly restriction
                        (valueInputElem as any).type = "checkbox"
                        break;
                }
            }
            return valueInputElem;
        },
    });
    
    const applyFilterBtnElem = containerElem.querySelector("#apply-filter-btn")!;
    applyFilterBtnElem.addEventListener("click", async () => {
        const loadingIndicatorElem = document.createElement("span");
        loadingIndicatorElem.classList.add("tw:loading", "tw:loading-spinner", "tw:loading-xs");
        applyFilterBtnElem.append(loadingIndicatorElem);
        
        try {
            const queryJSON = editor.toJson();
            const projectIDsResp = await postJson("/api/project/filter", {
                "query": queryJSON
            });
            const projectIDs = (await projectIDsResp.json()).ids;
        
            const projectsResp = await get(`/api/project/?all=true&ids=${projectIDs}`);
            const projects = await projectsResp.json()
            window.gridApi.setGridOption("rowData", projects);
        
            sessionStorage.setItem("filter:project-ids", projectIDs);
            
            // add filter indicator
            const projectFilterBtn = document.getElementById("project-filter-btn")! as HTMLElement;
            if (!projectFilterBtn.querySelector("span")) {
                const statusElem = document.createElement("span");
                statusElem.classList.add("tw:status", "tw:inline-block");
                projectFilterBtn.append(statusElem);
            }

        } catch (error) {}
        
        loadingIndicatorElem.remove();
        drawerElem.click();
    })

    const projectFilterDebugBtn = document.getElementById("debug-filter-btn")! as HTMLElement;
    projectFilterDebugBtn.addEventListener("click", () => {
        const queryJSON = editor.toJson();
        alert(gettext("生成的查询语句:") + "\n" + JSON.stringify(queryJSON, null, 2));
    })

    drawerElem.click();
}


function popupExportDataModal() {
    const containerElem = document.createElement("div");
    containerElem.innerHTML = `
<div class="tw:flex tw:flex-row-reverse tw:gap-2">
    <button id="download-empty-sample-file-btn" class="by6-crumbs-action-btn">
      ${gettext("下载样例文件")}
    </button>
    <button id="export-project-data-btn" class="by6-crumbs-action-btn">${gettext("导出")}</button>
</div>


<div>
  <h2 class="tw:text-lg tw:font-bold">${gettext("选择需要导出的部分")}<h2>
  <div>${gettext("导出的数据对应表格中(经过筛选)的数据")}</div>
  <br />
  <div class="tw:flex tw:flex-wrap tw:gap-2">
    <input type="checkbox" id="export-Project" aria-label="${gettext("基本信息")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-Plan" aria-label="${gettext("方案")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-InitiationData" aria-label="${gettext("立项资料")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-EthicalReview" aria-label="${gettext("伦理审核")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-HumanGeneticResource" aria-label="${gettext("人遗申请")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-HGRInternationalCooperation" aria-label="${gettext("人遗-国际合作批准")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-HGRCollection" aria-label="${gettext("人遗-采集批准")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-HGRStorage" aria-label="${gettext("人遗-保藏批准")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-HGRMaterialExport" aria-label="${gettext("人遗-材料出境批准")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-HGRDataFiling" aria-label="${gettext("人遗-数据备案批准")}"
      class="tw:btn tw:btn-sm" checked/>


    <input type="checkbox" id="export-Contract" aria-label="${gettext("合同签署")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-BeforeImplement" aria-label="${gettext("启动前准备")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-Material" aria-label="${gettext("物资管理")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-Beginning" aria-label="${gettext("启动")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-Implement" aria-label="${gettext("入组")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-QualityControl" aria-label="${gettext("质控")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-Finish" aria-label="${gettext("结束")}"
      class="tw:btn tw:btn-sm" checked/>
    <input type="checkbox" id="export-Examination" aria-label="${gettext("现场检查")}"
      class="tw:btn tw:btn-sm" checked/>
  </div>
</div>
`;

    createModal({
        contentElem: containerElem
    });

    const exportProjectDataBtnElem = document.getElementById("export-project-data-btn")!;
    exportProjectDataBtnElem.addEventListener("click", async () => {
        let projectIDs: number[] = [];
        window.gridApi.forEachNodeAfterFilter((rowNode, _) => {
            projectIDs.push(rowNode.data.id);
        })
        
        let exportModels: string[] = [];
        const itemInputs = containerElem.querySelectorAll("input:checked");
        itemInputs.forEach(item => {
            exportModels.push(item.id.split("-")[1]);
        });

        const response = await postJson("/api/project/export", {
            ids: projectIDs,
            models: exportModels
        })

        const contentDisposition = response.headers.get('content-disposition');
        const filename = contentDisposition?.split('filename=')[1]?.replace(/"/g, '')
            || 'export.xlsx';

        const blob = await response.blob();
        const url = window.URL.createObjectURL(blob);
        FileHandler.downloadFile(url, filename);

        const modalCloseBtnElem = document.getElementById("by6_modal_js_close_btn");
        modalCloseBtnElem?.click();
    });

    const downloadEmptySampleFileBtn = document.getElementById("download-empty-sample-file-btn")!;
    downloadEmptySampleFileBtn.addEventListener("click", async () => {
        let exportModels: string[] = [];
        const itemInputs = containerElem.querySelectorAll("input:checked");
        itemInputs.forEach(item => {
            exportModels.push(item.id.split("-")[1]);
        });
        const response = await postJson("/api/project/export", {
            sample: true,
            models: exportModels
        })

        const contentDisposition = response.headers.get('content-disposition');
        const filename = contentDisposition?.split('filename=')[1]?.replace(/"/g, '')
            || 'sample.xlsx';

        const blob = await response.blob();
        const url = window.URL.createObjectURL(blob);
        FileHandler.downloadFile(url, filename);

        const modalCloseBtnElem = document.getElementById("by6_modal_js_close_btn")!;
        modalCloseBtnElem.click();
    });

}

function popupImportDataModal() {
    const containerElem = document.createElement("div");
    containerElem.innerHTML = `
<div class="tw:flex tw:flex-row-reverse tw:gap-2">
    <button id="import-project-data-btn" class="by6-crumbs-action-btn">
      ${gettext("导入")}
    </button>
</div>

<div>
  <div>
  ${gettext("选择文件上传")} <span class="tw:text-sm tw:text-gray-500">${gettext("注意一行如未填写ID，则表示此行为新增数据")}</span>
  <br />
  <span class="tw:text-sm tw:text-gray-500">${gettext("提示: 可以到“导出”界面下载示例文件")}</span>
  </div>
  <input id="import-project-data-file-input" type="file" class="tw:file-input" />
  <div id="import-project-data-preview-area" class="tw:mt-2"></div>
</div>
`;

    createModal({
        contentElem: containerElem
    });

    const importDataBtnElem = document.getElementById("import-project-data-btn")!;
    importDataBtnElem.addEventListener("click", async () => {
        const importDataFileInputElem =
              containerElem.querySelector("#import-project-data-file-input") as HTMLInputElement;
        if (importDataFileInputElem.files!.length == 0) {
            alert(gettext("请选择上传的文件!"));
            return;
        }

        const loadingIndicatorElem = document.createElement("span");
        loadingIndicatorElem.classList.add("tw:loading", "tw:loading-spinner", "tw:loading-xs");
        importDataBtnElem.append(loadingIndicatorElem);

        // dry run for preview import errors and data changes
        const formData = new FormData();
        formData.append('file', importDataFileInputElem.files![0]);
        formData.append('dry_run', 'true');

        let response;
        try  {
            response = await request("/api/project/import", {
                method: 'POST',
                body: formData,
            });
        } catch (e: any) {
            loadingIndicatorElem.remove();
            throw e;
        }
        const respData = await response.json();

        const importDataPreviewAreaElem =
              containerElem.querySelector("#import-project-data-preview-area")!;
        const tabPreviewElem = renderImportDataPreview(respData);
        importDataPreviewAreaElem.replaceChildren(tabPreviewElem);

        

        const confirmImportBtn =
              tabPreviewElem.querySelector("#confirm-import-project-data-btn");
        confirmImportBtn?.addEventListener("click", async () => {
            const formData = new FormData();
            formData.append('file', importDataFileInputElem.files![0]);
            formData.append('dry_run', 'false');

            const response = await request("/api/project/import", {
                method: 'POST',
                body: formData,
            });

            // error handling, though it's logically impossible since we only
            // let user import data if there is no error in the preview step
            if (response.status == 400) {
                const tabPreviewElem = renderImportDataPreview(respData);
                importDataPreviewAreaElem.replaceChildren(tabPreviewElem);
                return;
            }

            alert(gettext("导入成功!"));
            const modalCloseBtnElem = document.getElementById("by6_modal_js_close_btn");
            modalCloseBtnElem?.click();
        });
        

        loadingIndicatorElem.remove();
    })
}

type ImportFeedbackRowData = {
    'row_number': number,
    'errors': string[],
    'validation_error': string,
    'diff': string[]
}

type ImportFeedbackModelData = {
    'model': string,
    'total_rows': number,
    'diff_headers': string[],
    'errors': ImportFeedbackRowData[],
    'diffs': ImportFeedbackRowData[]
}

type ImportFeedbackData = {
    'results': ImportFeedbackModelData[]
}

function renderImportDataPreview(data: ImportFeedbackData) {
    const containerElem = document.createElement("div");
    // containerElem.classList.add("tw:relative", "tw:overflow-auto");
    
    const tabsContainerElem = document.createElement("div");
    tabsContainerElem.classList.add("tw:tabs", "tw:tabs-box");
    const modelsData = data['results'];
    let hasError = false;
    modelsData.forEach((modelData, index) => {
        const modelDataHasError = modelData.errors.length !== 0;
        if (modelDataHasError) hasError = true;
        tabsContainerElem.insertAdjacentHTML(
            'beforeend',
            // use `style` to change --color-base-content
            `<input type="radio" name="tabs-import-data-preview"
               class="tw:tab" ${modelDataHasError ? 'style="--color-base-content: red;"' : ""}
               ${index == 0 ? 'checked="checked"' : ''}
               aria-label="${modelData.model}" />`
        );
        tabsContainerElem.append(renderImportDataPreviewSingleTabContent(modelData));
    });
    
    if (hasError) {
        containerElem.insertAdjacentHTML('beforeend', `
<span class="tw:text-error">${gettext("当前导入存在错误，请修改错误后重新导入")}</span>
        `);
    } else {
        containerElem.insertAdjacentHTML('beforeend', `
<div class="tw:mb-2">
<span class="tw:font-bold">是否确认导入</span>
<button class="by6-crumbs-action-btn" id="confirm-import-project-data-btn">${gettext("导入")}<button>
</div>`);
    }

    containerElem.append(tabsContainerElem);
    return containerElem;
}

function renderImportDataPreviewSingleTabContent(data: ImportFeedbackModelData) {
    const containerElem = document.createElement("div");
    containerElem.classList.add(
        "tw:tab-content", "tw:bg-base-100", "tw:border-base-300", "tw:p-6",
    )

    // error
    let runtimeErrorHTML = '';
    let validationErrorHTML = '';
    
    data.errors.forEach(rowData => {
        runtimeErrorHTML += rowData.errors.map(e =>
            `<li>${gettext("行")} ${rowData.row_number}: ${e}</li>`
        ).join("");

        validationErrorHTML += rowData.validation_error ?
            `<li>${gettext("行")} ${rowData.row_number}: ${rowData.validation_error}</li>`
            : ""
    })
    
    const hasError = runtimeErrorHTML !== "" || validationErrorHTML !== "";
    
    const errorHTML = `
<div class="tw:alert tw:alert-error tw:alert-soft tw:overflow-x-auto
  tw:flex tw:flex-col tw:gap-2 tw:items-start">

${
    runtimeErrorHTML !== '' ?
    `<h1 class="tw:text-lg">${gettext("运行错误列表")}</h1>
<ul>
  ${runtimeErrorHTML}
</ul>`: ''
}

${
    validationErrorHTML !== '' ?
    `
<h1 class="tw:text-lg">${gettext("验证错误列表")}</h1>

<ul>
  ${validationErrorHTML}
</ul>` : ''
}

</div>
`;
    

    // diff 
    const tHeadHTML = `
<thead>
  <tr>
    <th>${gettext("行号")}</th>
    ${data.diff_headers.map(s => `<th>${s}</th>`).join("")}
  </tr>
</thead>
`;

    const tBodyHTML = `
<tbody>
  ${data.diffs.map(rowData =>`
<tr>
  <th>${rowData.row_number}</th>
  ${rowData.diff.map(s => `<td>${s}</td>`).join("")}
</tr>
`).join("")}
</tbody>
`;
    
          
    containerElem.innerHTML = `
<div class="tw:flex tw:flex-col tw:gap-2">
  ${hasError ?
      `<h1 class="tw:text-lg tw:font-bold">${gettext("错误列表")}</h1> ${errorHTML} `
      : ""
  }

  <h1 class="tw:text-lg tw:font-bold">${gettext("更改预览")}</h1>

  <div class="tw:overflow-x-auto tw:rounded-box tw:border tw:border-base-content/5 tw:bg-base-100">
    <table class="tw:table tw:table-xs">
      ${tHeadHTML}
      ${tBodyHTML}
    </div>
  </div>
</div>
`;
    return containerElem;
}

(function () {
    ready(() => {
        const initialProjectIDs = sessionStorage.getItem("filter:project-ids");
        if (initialProjectIDs) {
            window.gridApi = initializeTable(`/api/project/?all=true&ids=${initialProjectIDs}`);
        } else {
            window.gridApi = initializeTable("/api/project/?all=true");
        }
        
        const projectFilterBtn = document.getElementById("project-filter-btn")! as HTMLElement;

        if (initialProjectIDs) {
            const statusElem = document.createElement("span");
            statusElem.classList.add("tw:status", "tw:inline-block");
            projectFilterBtn.append(statusElem);
        }
        
        projectFilterBtn.addEventListener("click", () => {
            popupFilterUI();
        });


        const popupExportProjectDataModalBtn =
              document.getElementById("popup-export-data-modal-btn")!;
        popupExportProjectDataModalBtn.addEventListener("click", () => {
            popupExportDataModal();
        });

        const popupImportProjectDataModalBtn =
              document.getElementById("popup-import-data-modal-btn")!;
        popupImportProjectDataModalBtn.addEventListener("click", () => {
            popupImportDataModal();
        });
    });
})();


